﻿using demo1;
using Microsoft.Win32;
using Modbus;
using Modbus.Device;
using Modbus.IO;
using Modbus.Message;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Demo1
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// 准备参数
        /// </summary>
        private String[] connectType = { "SerialPort" };
        private int[] serialBaud = { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 230400, 25600 };
        private int[] serialDataBit = { 5, 6, 7, 8 };
        private double[] serialStopBitCbb = { 1, 1.5, 2 };
        private StopBits[] serialStopBit = { StopBits.One, StopBits.OnePointFive, StopBits.Two };
        private String[] serialParityCbb = { "None Parity", "Odd Parity", "Even Parity" };
        private Parity[] serialParity = { Parity.None, Parity.Odd, Parity.Even };

        private String[] op_string = { "读线圈(01H)", "读离散输入(02H)", "读保持寄存器(03H)", "读输入寄存器(04H)","写单个线圈(05H)",
            "写单个保持寄存器(06H)","写多个线圈(0FH)","写多个保持寄存器(10H)" };
        private byte[] op_code = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0f, 0x10 };

        private SerialPort m_serialPort = null;
        private ModbusSerialMaster m_serialMaster;
        private ModbusSerialMaster rtu_serialMaster;
        private ModbusSerialMaster asiic_serialMaster;

        private bool isTransPortConnect = false;

        public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            m_serialPort = new SerialPort();

            //ui 连接类型
            connectType_cbb.DataSource = connectType;

            //ui 串口号加载
            //方式1
            //serialPort_cbb.DataSource = SerialPort.GetPortNames();
            //serialPort_cbb.SelectedIndex = 0;

            // UI 串口号加载
            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");
            if (keyCom != null)
            {
                string[] subKeys = keyCom.GetValueNames();         //获取所有存在的串口号并以数组暂存

                serialPort_cbb.Items.Clear();
                foreach (string name in subKeys)					//循环将端口信息添加到cobport中
                {
                    string sValue = (string)keyCom.GetValue(name);
                    serialPort_cbb.Items.Add(sValue);
                }
                serialPort_cbb.SelectedIndex = 0;
            }

            //波特率
            //serialBaud_cbb.DataSource = serialBaud;
            //serialBaud_cbb.SelectedIndex = 5;

            //数据位
            //serialDatabit_cbb.DataSource = serialDataBit;
            //serialDatabit_cbb.SelectedIndex = 3;

            //校验位
            //serialParity_cbb.DataSource = serialParityCbb;
            //serialParity_cbb.SelectedIndex = 0;

            //停止位
            //serialStopbit_cbb.DataSource = serialStopBitCbb;
            //serialStopbit_cbb.SelectedIndex = 0;


            //波特率
            foreach (var baud in serialBaud)
            {
                serialBaud_cbb.Items.Add(baud + " Baud");
            }
            serialBaud_cbb.SelectedIndex = 5;

            //数据位
            foreach (var dataBit in serialDataBit)
            {
                serialDatabit_cbb.Items.Add(dataBit + " Data Bits");
            }
            serialDatabit_cbb.SelectedIndex = 3;

            //校验位
            foreach (var parity in serialParityCbb)
            {
                serialParity_cbb.Items.Add(parity);
            }
            serialParity_cbb.SelectedIndex = 0;

            //停止位
            foreach (double stopBit in serialStopBitCbb)
            {
                serialStopbit_cbb.Items.Add(stopBit + " Stop Bit");
            }
            serialStopbit_cbb.SelectedIndex = 0;

            option_cbb.DataSource = op_string;

            state_tssl.Text = $"软件初始化成功!";

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_serialPort != null && m_serialPort.IsOpen)
            {
                m_serialPort?.Close();
                isTransPortConnect = false;
                //以上相当于
                //if(m_serialPort!=null)
                //{
                //m_serialPort.Close();
                //}
            }
        }

        /// <summary>
        /// 连接或断开
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void connect_btn_Click(object sender, EventArgs e)
        {
            //switch
            if (connectType_cbb.Text == connectType[0]) //判断是否是串口
            {
                if (!m_serialPort.IsOpen)
                {
                    try
                    {
                        m_serialPort.PortName = serialPort_cbb.Text;
                        m_serialPort.BaudRate = serialBaud[serialBaud_cbb.SelectedIndex];
                        m_serialPort.DataBits = serialDataBit[serialDatabit_cbb.SelectedIndex];
                        m_serialPort.StopBits = serialStopBit[serialStopbit_cbb.SelectedIndex];
                        m_serialPort.Parity = serialParity[serialParity_cbb.SelectedIndex];

                        m_serialPort.Open();

                        #region 桥接设计模式
                        ////桥接设计模式
                        //// 实例化一个串口实现,实现了IStreamResource
                        //SerialPortAdapter serialPortAdapter = new SerialPortAdapter(m_serialPort);
                        //// 实例化transport时可以配置不同的实现
                        //// 1,串口RTU方式 
                        //ModbusTransport rtuTransport = new ModbusRtuTransport(serialPortAdapter);
                        //// 2,串口Ascii方式
                        //ModbusTransport asciiTransport = new ModbusAsciiTransport(serialPortAdapter);
                        //// 3.通过串口的方式来传输IpTransport形式的数据帧
                        //ModbusTransport tcpTransport1 = new ModbusIpTransport(serialPortAdapter);


                        //TcpClient tcp = new TcpClient();
                        //TcpClientAdapter tcpClientAdapter = new TcpClientAdapter(tcp);
                        ////ip形式的数据帧
                        //ModbusTransport tcpTransport = new ModbusIpTransport(tcpClientAdapter);
                        ////tcp rtu
                        //ModbusTransport rtuTcpTransport = new ModbusRtuTransport(tcpClientAdapter);
                        ////tcp ascii
                        //ModbusTransport asciiTcpTransport = new ModbusAsciiTransport(tcpClientAdapter);
                        #endregion

                        rtu_serialMaster = ModbusSerialMaster.CreateRtu(m_serialPort);
                        asiic_serialMaster = ModbusSerialMaster.CreateAscii(m_serialPort);

                        //这里涉及到切换,所以同时要注册两个事件
                        rtu_serialMaster.Transport.ModbusTransportWriteCompleted += new EventHandler<ModbusTransportEventArgs>(writeCompleted);
                        asiic_serialMaster.Transport.ModbusTransportWriteCompleted += new EventHandler<ModbusTransportEventArgs>(writeCompleted);


                        if (RTU_rbtn.Checked)
                        {
                            m_serialMaster = rtu_serialMaster;
                        }

                        if (ASCII_rbtn.Checked)
                        {
                            m_serialMaster = asiic_serialMaster;
                        }

                        //事件订阅器
                        //订阅ModbusTransport类定义的ModbusTransportWriteCompleted事件
                        //m_serialMaster.Transport.ModbusTransportWriteCompleted +=
                        //    new EventHandler<ModbusTransportEventArgs>(writeCompleted);
                        //配置重复发送(如果发送失败重新发送的次数)为2次
                        m_serialMaster.Transport.Retries = 2;
                        //写入超时2秒
                        m_serialMaster.Transport.WriteTimeout = 2000;
                        //读取超时2秒
                        m_serialMaster.Transport.ReadTimeout = 2000;

                        //m_serialMaster.Transport.ModbusTransportWriteCompleted += (obj, args) =>
                        //{
                        //    //发送指令完成事件回调,通知我们指令发送完成
                        //    //显示发送的指令数据帧
                        //    byte[] data = args.Message;
                        //    sendcmd_rtb.Text = Transform.ToHexString(args.Message, " ");

                        //};

                        connect_btn.Text = "断开";
                        isTransPortConnect = true;
                        state_tssl.Text = $"串口{m_serialPort.PortName}已经连接";

                    }
                    catch (Exception)
                    {

                        throw;
                    }
                }
                else
                {
                    try
                    {
                        if (m_serialPort.IsOpen)
                        {
                            m_serialPort.Close();
                            isTransPortConnect = false;
                        }
                        connect_btn.Text = "连接";
                        state_tssl.Text = $"串口{m_serialPort.PortName}已经断开";

                    }
                    catch (Exception)
                    {

                        throw;
                    }
                }
            }
        }

        //发送指令完成事件回调,通知我们指令发送完成
        private void writeCompleted(object sender, ModbusTransportEventArgs e)
        {
            //显示发送的指令数据帧
            //RTU
            if (RTU_rbtn.Checked)
            {
                sendcmd_rtb.Text = Transform.ToHexString(e.Message, " ");
            }

            //ASCII
            if (ASCII_rbtn.Checked)
            {
                sendcmd_rtb.Text = Encoding.ASCII.GetString(e.Message);
            }

        }



        /// <summary>
        /// 读取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void read_btn_Click(object sender, EventArgs e)
        {
            if (!isTransPortConnect)
            {
                MessageBox.Show("请检查连接状态!");
                return;
            }

            byte station = Convert.ToByte(station_txb.Text);
            ushort startAddress = Convert.ToUInt16(startAddress_txb.Text);
            ushort num = UInt16.Parse(num_txb.Text);

            //这里操作会卡界面(UI线程),所以开一个新线程
            Task.Run(new Action(() =>
            {
                try
                {
                    switch (op_code[option_cbb.SelectedIndex])
                    {
                        case Modbus.Modbus.ReadCoils:
                            //读线圈(01H)
                            bool[] result = m_serialMaster.ReadCoils(station, startAddress, num);
                            recivecmd_rtb.Text = String.Join(",", result);
                            break;
                        case Modbus.Modbus.ReadInputs:
                            //读离散输入(02H)
                            bool[] result1 = m_serialMaster.ReadInputs(station, startAddress, num);
                            recivecmd_rtb.Text = String.Join(",", result1);
                            break;
                        case Modbus.Modbus.ReadHoldingRegisters:
                            //读保持寄存器(03H)
                            ushort[] result2 = m_serialMaster.ReadHoldingRegisters(station, startAddress, num);
                            recivecmd_rtb.Text = String.Join(",", result2);
                            break;
                        case Modbus.Modbus.ReadInputRegisters:
                            //读输入寄存器(04H)
                            ushort[] result3 = m_serialMaster.ReadInputRegisters(station, startAddress, num);
                            recivecmd_rtb.Text = String.Join(",", result3);
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }

            }));


        }

        /// <summary>
        /// 写入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void write_btn_Click(object sender, EventArgs e)
        {
            if (!isTransPortConnect)
            {
                MessageBox.Show("请检查连接状态!");
                return;
            }

            byte station = Convert.ToByte(station_txb.Text);
            ushort startAddress = Convert.ToUInt16(startAddress_txb.Text);

            Task.Run(() =>
            {
                try
                {
                    switch (op_code[option_cbb.SelectedIndex])
                    {
                        case Modbus.Modbus.WriteSingleCoil:
                            // m_serialMaster.WriteSingleCoil(station, startAddress, Convert.ToBoolean("True"));
                            {   //这里加{}作用域,是为了下面的case块res变量名不冲突
                                WriteSingleCoilRequestResponse res =
                                    m_serialMaster.WriteSingleCoilWithResponse(station, startAddress, Convert.ToBoolean(data_txb.Text));//自行加判断:true、false
                                //recivecmd_rtb.Text = Transform.ToHexString(m_serialMaster.Transport.BuildMessageFrame(res), " ");
                                recivecmd_rtb.Text = show_reciedcmd(res);
                            }
                            break;
                        case Modbus.Modbus.WriteSingleRegister:
                            //m_serialMaster.WriteSingleRegister(station, startAddress, 123);
                            {   //这里加{}作用域,是为了下面的case块res变量名不冲突
                                WriteSingleRegisterRequestResponse res =
                                    m_serialMaster.WriteSingleRegisterWithResponse(station, startAddress, Convert.ToUInt16(data_txb.Text));//自行加判断:ushort:0-65535
                                //recivecmd_rtb.Text = Transform.ToHexString(m_serialMaster.Transport.BuildMessageFrame(res), " ");
                                recivecmd_rtb.Text = show_reciedcmd(res);
                            }
                            break;
                        case Modbus.Modbus.WriteMultipleCoils:
                            //m_serialMaster.WriteMultipleCoils(station, startAddress, new bool[] { true, false, false, true });
                            {   //这里加{}作用域,是为了下面的case块res变量名不冲突

                                //数据包:"true|false|false|true"
                                WriteMultipleCoilsResponse res =
                                    m_serialMaster.WriteMultipleCoilsWithResponse(station, startAddress, Transform.ToBools(data_txb.Text, ','));
                                //recivecmd_rtb.Text = Transform.ToHexString(m_serialMaster.Transport.BuildMessageFrame(res), " ");
                                recivecmd_rtb.Text = show_reciedcmd(res);
                            }
                            break;
                        case Modbus.Modbus.WriteMultipleRegisters:
                            {   //这里加{}作用域,是为了下面的case块res变量名不冲突
                                //m_serialMaster.WriteMultipleRegisters(station, startAddress, new ushort[] { 123, 456, 789 });

                                //数据包:"123|456|789"
                                WriteMultipleRegistersResponse res = m_serialMaster.WriteMultipleRegistersWithResponse(station, startAddress,
                                    Transform.ToUInt16s(data_txb.Text, ','));
                                //recivecmd_rtb.Text = Transform.ToHexString(m_serialMaster.Transport.BuildMessageFrame(res), " ");
                                recivecmd_rtb.Text = show_reciedcmd(res);
                            }
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            });

        }

        /// <summary>
        /// 根据RTU或ASCII编码来显示接收的指令
        /// </summary>
        /// <returns></returns>
        private String show_reciedcmd(IModbusMessage res)
        {
            if (RTU_rbtn.Checked)
            {
                return Transform.ToHexString(m_serialMaster.Transport.BuildMessageFrame(res), " ");
            }
            else
            {
                return Encoding.ASCII.GetString(m_serialMaster.Transport.BuildMessageFrame(res));
            }
        }


        /// <summary>
        /// 根据控件选择是读还是写来禁用相关控件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void option_cbb_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (option_cbb.SelectedIndex <= 3)
            {
                data_txb.Enabled = false;
                write_btn.Enabled = false;
                read_btn.Enabled = true;
            }
            else
            {
                data_txb.Enabled = true;
                write_btn.Enabled = true;
                read_btn.Enabled = false;
            }

            //如果是"写单个线圈(05H)","写单个保持寄存器(06H)"
            if (option_cbb.SelectedIndex == 4 || option_cbb.SelectedIndex == 5)
            {
                num_txb.Enabled = false;
            }
            else
            {
                num_txb.Enabled = true;
            }
        }

        /// <summary>
        /// 选中RTU,切换ModbusMaster的通讯方式为rtu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RTU_rbtn_CheckedChanged(object sender, EventArgs e)
        {
            if (m_serialPort.IsOpen)
            {
                if (RTU_rbtn.Checked)
                {
                    m_serialMaster = rtu_serialMaster;
                }
                else
                {
                    m_serialMaster = asiic_serialMaster;
                }
            }
        }

        /// <summary>
        /// 选中ASCII,切换ModbusMaster的通讯为ascii
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ASCII_rbtn_CheckedChanged(object sender, EventArgs e)
        {
            if (m_serialPort.IsOpen)
            {
                if (ASCII_rbtn.Checked)
                {
                    m_serialMaster = asiic_serialMaster;
                }
                else
                {
                    m_serialMaster = rtu_serialMaster;
                }
            }
        }
    }
}
